{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 问题：hasattr() getattr() setattr()的用法"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "面试官想从这道题中考察面试人什么？\n",
    "这道题中的三个方法都是处理属性的内置方法，它们用于控制属性的访问权限以及实现动态属性，这也是元编程技术的一种，即它可以在运行时改变代码行为。\n",
    "\n",
    "答案\n",
    "hasattr(object, name)：判断一个对象里面是否有 name 属性或者 name 方法，返回布尔值，有name特性返回True， 否则返回False。\n",
    "\n",
    "getattr(object, name[,default])：获取对象 object 的属性或者方法，如果存在返回该属性值，如果不存在，返回默认值。默认值可选。如果没有设置默认值，且属性不存在会抛出AttributieError。\n",
    "\n",
    "setattr(object, name, values)：给对象的属性赋值，若属性不存在，先创建再赋值。\n",
    "\n",
    "拓展\n",
    "hasattr的实现方法其实是先调用getattr，看看是否抛出AttributieError，抛出则返回False，不抛出则返回True。\n",
    "\n",
    "也许有人会想，读取或写属性值直接用点符号.就可以了，比如：setattr(x, 'foobar', 123)和x.foobar = 123是一样的效果，为什么还需要这三个内置方法呢？因为，有了这些内置方法，我们可以这样做：\n",
    "\n",
    "def dynamic_call(obj, action_method, *args, **kwargs):\n",
    "    method = getattr(obj, action_method, None)\n",
    "    result = None\n",
    "    if method:\n",
    "        result = method(*args, **kwargs)\n",
    "\n",
    "    return result\n",
    "上面这段代码只是一个示例，演示了如何动态的调用实例方法。这样的好处就是，不用写一些重复的代码，比较灵活。\n",
    "\n",
    "再看另外一个例子：\n",
    "\n",
    ">>> class myobj():\n",
    "...    pass\n",
    ">>> kwargs = {'username': \"bob\", 'email': \"bob@example.com\"}\n",
    ">>> for key, value in kwargs.items():\n",
    "...    setattr(myobj, key, value)\n",
    ">>> myobj.username\n",
    ">>> myobj.email\n",
    "在前面的章节中（第 5 小节中的自省函数）也有相关的示例，大家也可以参考一下。\n",
    "\n",
    "另外，我们需要知道，getattr(object, name）的背后实际上是触发Class.__getattribute__(object, name）方法。如果调用__getattribute__抛出了AttributieError，这个时候会继续去调用__getattr__查找属性。而setattr(object, name, values）的背后实际上是触发Class.__setattribute__(object, name, values）方法。我们为什么需要知道这些呢？因为如果我们自己去实现一个类，希望别人去按你所期望去使用getattr和setattr，那知道一些这背后的调用机制就非常有必要了。\n",
    "\n",
    "问题：如何实现Python函数重载\n",
    "面试官想从这道题中考察面试人什么？\n",
    "这道题可能会让答题者疑惑，因为 Python 本身是不支持函数重载的，但是functools.singledispatch装饰器可以间接实现，所以，这道题考大家对Python函数重载的\n",
    "基本认识，另外一个考大家的单分派函数的用法。\n",
    "\n",
    "答案\n",
    "Python 重载机制: 转发（Dispatch），即通过使用单分派函数functools.singledispatch来实现函数重载。\n",
    "\n",
    "拓展\n",
    "函数重载，就是指 函数名字相同但参数类型或个数不同时执行不同的函数。目前为止，Python 本身是不支持重载的，只有使用functools这个库的singledispatch才可以间接实现。\n",
    "\n",
    "使用单分派函数实现函数重载，主要有两个关键点：\n",
    "\n",
    "使用@singledispatch装饰器标记处理基函数\n",
    "使用@<<base_function>>.register(<<type>>)装饰各个专门的函数\n",
    "具体使用看下面这个示例：\n",
    "\n",
    "@singledispatch\n",
    "def to_str(obj):\n",
    "    print('%r'%(obj))\n",
    "\n",
    "# to_str是上面标记的基函数，函数参数类型是作为register的参数传入，从而实现重载\n",
    "@to_str.register(int)\n",
    "def _(obj): # 专门函数的名称无关紧要，所以使用`_`这个占位符即可。并且切记不要和基函数名字一样，否则会覆盖。\n",
    "    print('Integer: %d'%(obj))\n",
    "\n",
    "@to_str.register(str)\n",
    "def _(obj):\n",
    "    print('String: %s'%(obj))\n",
    "\n",
    "@to_str.register(list)\n",
    "def _(obj):\n",
    "    print('List: %r'%(obj))\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    to_str(1)\n",
    "    to_str('hello')\n",
    "    to_str(range(3))\n",
    "    to_str(object)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}